//二、作用域

//作用域:  程序源代码中定义变量的区域
// 作用域规定了如何查找变量，也就是确定当前执行代码对变量的访问权限
//JavaScript 采用词法作用域(lexical scoping)，也就是静态作用域

//静态作用域与动态作用域

//JavaScript 采用的是词法作用域(静态作用域)
// 函数的作用域在函数定义的时候就决定了
//变量查找基于代码的书写位置

var value = 1;
function foo(){
    console.log(value);
}
function bar(){
   var value = 2;
    foo();
}

bar();  // 1
/*
执行过程
*1.执行 bar() 函数
*2.在 bar() 中调用 foo()
*3.foo() 内部查找变量 value
*4.由于 JavaScript 是词法作用域，foo() 的作用域在定义时确定
*5.查找上层作用域，找到全局变量 value = 1
*6.输出 1
* */




//静态作用域类型:

// 全局作用域:
var globalVariable = "我在全局作用域";
//在任何地方都可以访问

// 函数作用域:
function test() {
    var functionScoped = "我在函数作用域";
    // 只能在函数内部访问
}


// 块级作用域:
{
    let blockScoped = "我在块级作用域";
    const alsoBlockScoped = "我也是";
    // 只能在 {} 内部访问
}

// 作用域链机制（查找规则）
//1.在当前作用域查找变量
//2.如果找不到，向上一层作用域查找
//3.持续向上直到全局作用域
//4.如果全局作用域也找不到，报 ReferenceError

//静态作用域相关实际应用场景
// 闭包与作用域
function createCounter() {
    let count = 0; // 在 createCounter 作用域内

    return function() {
        count++; // 访问父作用域的变量
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// count 变量在闭包中被"记住"

//模块模式
const calculator =
    (function() {
        let result = 0; // 私有变量
        return {
            add: function(x) {
                result += x;
                return this;
            },
            getResult: function() {
                return result;
            }
        };
    })();
calculator.add(5).add(3);
console.log(calculator.getResult()); // 8

//块级作用域
// let/const 具有块级作用域
function demonstrateBlockScope() {
    if (true) {
        let blockScoped = "我在块内";
        var functionScoped = "我在函数内";
    }

    // console.log(blockScoped); // ReferenceError
    console.log(functionScoped); // "我在函数内"
}


//动态作用域:
// 函数的作用域是在函数调用的时候才决定的
//变量查找基于调用链
//与函数定义位置无关

/*
把下面的脚本存成例如 scope.bash，然后进入相应的目录，用命令行执行 bash ./scope.bash
value=1
function foo () {
    echo $value;
}
function bar () {
    local value=2;
    foo;
}
bar
*/

/*
* 执行过程（变量查找基于调用链）
* 1.执行 bar() 函数
* 2.在 bar() 中调用 foo()
* 3.foo() 内部查找变量 value
* 4.从调用函数 bar() 的作用域中查找
* 5.找到 bar() 中的 value = 2
* 6.输出 2
* */
